/**
 * @file .
 * @author wangxiaowei0303@163.com
 */

#include <time.h>
#ifdef ENABLE_DEBUG
#include <stdio.h>
#endif

#include <random>
#include <opencv2/core/core.hpp>

#include "ImpulseNoise.h"
#include "../string_util.h"
#include "../opencv_util.h"

/*
 * SALT;为 this->saltp 参数呈现给用户的名称.
 * PEPPER;为 this->pepperp 参数呈现给用户的名称.
 */
#define SALT "saltp"
#define PEPPER "pepperp"

ImpulseNoise::~ImpulseNoise()
{
    ;
}

void
ImpulseNoise::help(std::string &buf) const
{
    buf.append(
        "ImpulseNoise;为图像增加椒盐噪声;支持参数如下:\n"
        SALT ";盐噪声发生的概率;double 类型;范围:[0,1].\n"
        PEPPER ";椒噪声发生的概率;double 类型;范围:[0,1]\n"
    );
    return ;
}

/*
 * 处理不合法的参数名.
 * 这里'不合法的参数名'指的是当前算法不支持的参数.
 */
static void
handleInvalidArg(const char *fun,const char *arg)
{
    std::string err("ERROR;不合法的参数 ");
    err += arg;
    err += " 出现在函数 ";
    err += fun;
    err.push_back('\n');
    throw std::runtime_error(err);
}

void
ImpulseNoise::set(const char *argname,const char *argval)
{
    if (strEqual(argname,SALT))
        this->saltp = str2double(argval);
    else if (strEqual(argname,PEPPER)) 
        this->pepperp = str2double(argval);
    else 
        handleInvalidArg(__PRETTY_FUNCTION__,argname);
}

const char*
ImpulseNoise::get(const char *argname,std::string &buf) const
{
    size_t oldsize = buf.size();
    if (strEqual(argname,SALT)) 
        addFormat(buf,"%f",this->saltp);
    else if (strEqual(argname,PEPPER))
        addFormat(buf,"%f",this->pepperp);
    else
        return 0;
    return buf.data() + oldsize;
}

void
ImpulseNoise::getSupportedArgs(std::vector<std::string> &arglist) const
{
    arglist.push_back(SALT);
    arglist.push_back(PEPPER);
    return ;
}

void
ImpulseNoise::process(cv::Mat &img)
{
    if (img.channels() != 1 || img.depth() != CV_8U) {
        std::string err;
        addFormat(err,
            "ERROR;ImpulseNoise::process(): 不支持的图像格式;"
            "channels: %d(支持: 1);"
            "depth: %s(支持: CV_8U)\n",
            img.channels(),
            depth2str(img.depth()));
        throw std::runtime_error(err);    
    }

    size_t salt_pixel_num = img.total() * this->saltp;
    size_t pepper_pixel_num = img.total() * this->pepperp;
    
    std::mt19937_64 engine(time(0));
    std::uniform_int_distribution<size_t> xrand(0,img.cols - 1);
    std::uniform_int_distribution<size_t> yrand(0,img.rows - 1);
    
    for (size_t i = 0; i < salt_pixel_num; ++i) 
        img.at<unsigned char>(yrand(engine),xrand(engine)) = 255;
    for (size_t i = 0; i < pepper_pixel_num; ++i)
        img.at<unsigned char>(yrand(engine),xrand(engine)) = 0;
    
    return ;
}

NOISE_ALGO_REGISTER("ImpulseNoise",ImpulseNoise);



